/* Static */
local AdvTimers					= {}

/* CTOR */
AdvTimer						= class( function( self, ... )

	/* Default values */
	self.ID						= nil
	self.Interval				= 0.01
	self.Duration				= 1.0
	self.Elapsed				= 0
	self.LastTime				= CurTime()
	self.LoopCount				= 1
	self.CurrentLoops			= 0
	self.SpeedRatio				= 1
	self.IsRunning				= false
	self.IsPaused				= false
	self.LinkedObject			= nil
	
	/* Read parameters */
	local intervalParam			= select( 1, ... )
	local durationParam			= select( 2, ... )
	local loopParam				= select( 3, ... )
	
	/* Assign interval */
	if IsNumber( intervalParam ) and intervalParam > 0 then
		self.Interval			= intervalParam
	end
	
	/* Assign duration */
	if IsNumber( durationParam ) and durationParam > 0 then
		self.Duration			= durationParam
	end
	
	/* Assign loop */
	if loopParam != nil then
	
		/* Predefined loop count */
		if IsInt( loopParam ) then
			self.LoopCount		= loopParam
			
		/* Infinite loop count */
		elseif tobool( loopParam ) then
			self.LoopCount		= 0
			
		/* Only once */
		else
			self.LoopCount		= 1
		end
	end
end)

/* Check if the timer is valid */
AdvTimer.IsValid = function( self )
	return timer.IsTimer( "AdvTimer_" .. tostring( self.ID ) ) and self.ID != nil
end

/* Create new ID / take unused one */
AdvTimer.GetID = function( advTimer )
	
	/* Variables */
	local timerID				= nil
	
	/* Determine timer ID */
	for i,v in ipairs( AdvTimers ) do
		
		/* Found empty slot */
		if AdvTimers[i] == nil then
			timerID				= i
			break
		end
	end
	
	/* Empty slot found ? Replace previous one */
	if timerID != nil then
		AdvTimers[timerID]		= advTimer
		
	/* No empty slot found ? Insert at the end */
	else
		timerID					= table.insert( AdvTimers, advTimer )
	end
	
	/* Set the timer's ID */
	advTimer.ID					= timerID
	
	/* Debug output */
	 if zed then zed = zed end -- DEBUG:Msg( "Got an ID: " .. tostring( timerID ), "AdvTimer.GetID" )
	
	/* Return the ID */
	return timerID
end

/* Unregisters a timer ID */
AdvTimer.RemoveID = function( advTimer )

	/* Invalid ID */
	if advTimer.ID == nil then
		 if zed then zed = zed end -- DEBUG:Msg( "Timer ID already removed, skipped.", "AdvTimer.RemoveID" )
		return
	end

	/* Create an empty slot */
	AdvTimers[advTimer.ID]		= nil
	
	/* Reset the timer's ID */
	advTimer.ID					= nil
end

/* Get frames from duration */
AdvTimer.GetTotalFrames = function( self )
	if self.LoopCount == 0 then return 0
	else return self.Duration / self.Interval end
end

/* Get elapsed frames */
AdvTimer.GetElapsedFrames = function( self )
	return self.Elapsed / self.Interval
end

/* Reset the timer */
AdvTimer.Reset = function( self )
	self.CurrentLoops			= 0
	self.Elapsed				= 0
end

/* Gets the interval of the last tick */
AdvTimer.RealInterval = function( self )
	return CurTime() - self.LastTime
end

/* Start the timer */
AdvTimer.Start = function( self )
	
	/* Register ID */
	AdvTimer.GetID( self )
	
	/* Reset time elapsed */
	self.Elapsed			= 0
	self.LastTime			= CurTime()
	
	/* If it is already running, just let it go */
	if self.IsRunning then
		
		/* Invalid timer ?? */
		if not self:IsValid() then
			DEBUG:ErrMsg( "Timer is invalid, can't restart.", "AdvTimer.Start" )
		else
			timer.Start( "AdvTimer_" .. tostring( self.ID ) )
		end
		
	/* Otherwise, create it and start it */
	else
		/* ID not set ?? */
		if self.ID == nil then
			DEBUG:ErrMsg( "ID not set, can't create timer.", "AdvTimer.Start" )
		else
			timer.Create( "AdvTimer_" .. self.ID, self.Interval, self:GetTotalFrames(), AdvTimer.Running, self )
			self.IsRunning		= true
		end
	end
end

/* Stop the timer */
AdvTimer.Stop = function( self )

	/* Check that timer is valid */
	if not self:IsValid() then
		 if zed then zed = zed end -- DEBUG:Msg( "Timer does not exist, skipping.", "AdvTimer.Stop" )
		self:Reset()
		return
	end
	
	/* If it is running, stop it */
	if self.IsRunning then
		timer.Stop( "AdvTimer_" .. self.ID )
		-- timer.Destroy( "AdvTimer_" .. self.ID )
		self.IsRunning			= false
		
		/* Unregister ID */
		AdvTimer.RemoveID( self )
	end
	
	/* Reset it */
	self:Reset()
end

/* Pause the timer */
AdvTimer.Pause = function( self )
	
	/* Check that timer is valid */
	if not self:IsValid() then
		DEBUG:ErrMsg( "Timer is not valid: " .. tostring( self.ID ), "AdvTimer.Pause" )
		return
	end
	
	/* Check if it is running */
	if self.IsRunning and not self.IsPaused then
	
		/* Pause it */
		timer.Pause( "AdvTimer_" .. self.ID )
		self.IsPaused			= true
	end
end

/* Resume the timer */
AdvTimer.Resume = function( self )
	
	/* Check that timer is valid */
	if not self:IsValid() then
		DEBUG:ErrMsg( "Timer is not valid: " .. tostring( self.ID ), "AdvTimer.Resume" )
		return
	end
	
	/* Check if it is running */
	if self.IsRunning and self.IsPaused then
	
		/* Unpause it */
		self.LastTime			= CurTime()
		timer.UnPause( "AdvTimer_" .. self.ID)
		self.IsPaused			= false
	end
end

/* Timer tick hook */
AdvTimer.Tick = function( self )

	/* Output */
	 if zed then zed = zed end -- DEBUG:Msg( "Timer " .. self.ID .. " TICK: " .. self.Elapsed .. "secs (frames: " .. self:GetElapsedFrames() .. ").", "AdvTimer.Tick" )
end

/* Timer ended hook */
AdvTimer.Ended = function( self )

	/* Output */
	 if zed then zed = zed end -- DEBUG:Msg( "Timer " .. self.ID .. " ENDED: ran " .. self.CurrentLoops .. " time(s)/" .. self.LoopCount, "AdvTimer.Ended" )
end

/* Timer running hook */
AdvTimer.Running = function( advTimer )

	/* If the timer is not valid, do nothing */
	if not advTimer:IsValid() then
		 if zed then zed = zed end -- DEBUG:Msg( "Timer is invalid, skipped.", "AdvTimer.Running" )
		return
	end
	
	/* Update time elapsed */
	advTimer.Elapsed			= advTimer.Elapsed + advTimer:RealInterval()
	
	/* Do nothing while we're not synchronized with the speed ratio */
	if math.floor( advTimer.Elapsed % ( ( 1 / advTimer.SpeedRatio ) * advTimer.Interval ) * 100) / 100 != 0 then
		return
	end
	
	/* Raise tick event */
	if advTimer.Tick then
		advTimer:Tick()
	end
	
	/* Save last tick time */
	advTimer.LastTime			= CurTime()
	
	/* Check if we've reached the end */
	if advTimer.Elapsed >= advTimer.Duration then
		
		/* Increase loops done */
		advTimer.CurrentLoops	= advTimer.CurrentLoops + 1
		
		/* Raise ended event */
		if advTimer.Ended then
			advTimer:Ended()
		end
		
		/* Update elapsed */
		advTimer.Elapsed = advTimer.Elapsed - advTimer.Duration
		
		/* If it should loop, restart it */
		if util.tobool( advTimer.LoopCount ) then
			
			/* If all loops are done, stop it */
			if advTimer.CurrentLoops >= advTimer.LoopCount then
				advTimer:Stop()
				
			/* Otherwise, restart it */
			-- else
				-- advTimer:Start()
			end
			
		/* In case of infinite loops, just restart it */
		-- elseif IsNumber( advTimer.LoopCount ) and advTimer.LoopCount == 0 then
			-- advTimer:Start()
			
		/* Otherwise, stop it */
		elseif not IsNumber( advTimer.LoopCount ) or advTimer.LoopCount != 0 then
			advTimer:Stop()
		end
	end
end